package com.zanglikun.framework.servlet;

import com.alibaba.fastjson.JSON;
import com.zanglikun.framework.model.UrlMapping;
import com.zanglikun.framework.utils.ClassUtils;
import com.zanglikun.framework.utils.DateTimeConverter;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @author : zanglikun
 * @date : 2021/2/23 14:46
 * @Version: 1.0
 * @Desc : 配置 DispatcherServlet
 */

public class DispatcherServlet extends HttpServlet {

    // 创建 收集数据的对象
    private static Map<String, UrlMapping> map = new HashMap<>();

    private static final String REQUEST_PREFIX = "prefix";  // 请求前缀
    private static final String REQUEST_SUFFIX = "suffix";   // 请求后缀
    private static final String BASKPACKAGE = "basePackage";    //controller 的包路径

    private String prefix = "/WEB-INF/jsp";
    private String suffix = ".jsp";
    private String basePackage = "com.zanglikun";

    // 可以处理的Java类型，作用是 如果不可以处理，就让Java对象处理
    Set<Class> doCopy = new HashSet<>();


    @Override
    public void init(ServletConfig config) {
        // 注册时间解析
        ConvertUtils.register(new DateTimeConverter(), LocalDate.class);
        ConvertUtils.register(new DateTimeConverter(), LocalDateTime.class);

        try {
            // 如果初始化 配置了值，就会使用web.xml 的信息
            if (config.getInitParameter(REQUEST_PREFIX) != null) {
                prefix = config.getInitParameter(REQUEST_PREFIX);
            }
            if (config.getInitParameter(REQUEST_SUFFIX) != null) {
                suffix = config.getInitParameter(REQUEST_SUFFIX);
            }
            if (config.getInitParameter(BASKPACKAGE) != null) {
                basePackage = config.getInitParameter(BASKPACKAGE);
            }
            map = ClassUtils.getUrlMappings(basePackage);

            // 记录 能处理的类型，作用是 如果方法参数类型 不是记录的值，就以Java 对象处理
            doCopy.add(java.lang.String.class);
            doCopy.add(java.lang.Integer.class);
            doCopy.add(java.lang.Long.class);
            doCopy.add(java.lang.Double.class);
            doCopy.add(java.lang.Float.class);
            doCopy.add(java.lang.Short.class);
            doCopy.add(java.lang.Byte.class);
            doCopy.add(java.lang.Boolean.class);
            doCopy.add(java.lang.Character.class);
            doCopy.add(java.time.LocalDateTime.class);
            doCopy.add(java.time.LocalDate.class);


        } catch (Exception e) {

        }

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        try {
            req.setCharacterEncoding("utf-8");
            resp.setContentType("text/html;charset=UTF-8");

            /** 处理请求路径 */
            String requestURI = req.getRequestURI();
            // 调用我们的 工具类 通过 请求的相对路径 获取 需要执行操作 类的信息
            UrlMapping urlMapping = map.get(requestURI.replace(req.getContextPath(), ""));

            // 人为 测试 制作服务端错误 500
            //int i = 1/0;

            // 如果 请求路径不存在
            if (urlMapping == null) {
                resp.setStatus(404);
                resp.getWriter().write("请求路径不存在：<h1>404</h1> 请求路径是" + req.getRequestURI());
                // 结束 下面代码执行
                return;
            }

            /** 处理请求参数 */
            Method method = urlMapping.getMethod();
            Object obj = urlMapping.getObj();
            Map<String, Class<?>> parameters = urlMapping.getParameter();
            // 创建 数组 将来放参数的地方
            Object[] argurments = new Object[method.getParameterTypes().length];
            // 定义一个 下标
            int index = 0;

            for (String key : parameters.keySet()) {
                // 请求的参数名称是 key

                // 获取方法参数的Java类型
                Class<?> parameterType = parameters.get(key);

                // 请求参数值 通过request 获取与 方法参数名称匹配的 参数值
                String parameter = req.getParameter(key);

                // 请求参数是数组形式的 请求参数值
                String[] parameterValues = req.getParameterValues(key);

                // 获取数组的长度
                int shuzulength = 0;
                if(parameterValues != null){
                    shuzulength = parameterValues.length;
                }
                // 请求参数如果是数组形式 获取数组数据类型
                Class<?> shuzuType = parameterType.getComponentType();


                // 如果是 HttpServletRequest 对象 就放进去 doGet的 req
                if (parameterType == HttpServletRequest.class) {
                    argurments[index++] = req;
                    continue;
                }

                // 如果是 HttpServletResponse 对象 就放进去 doGet的 resp
                if (parameterType == HttpServletResponse.class) {
                    argurments[index++] = resp;
                    continue;
                }

                if (parameter != null) {
                    // 这里 是处理 8大基本类型 与String 利用请求参数类型与 方法参数类型进行匹配 成功就赋值
                    try {

                        if (parameterType == java.lang.String.class) {
                            argurments[index++] = parameter;
                            continue;
                        }

                        /** 基本数据类型转换开始 */
                        if (parameterType == java.lang.Integer.class) {
                            argurments[index++] = Integer.parseInt(parameter);
                            continue;
                        }
                        if (parameterType == java.lang.Double.class) {
                            argurments[index++] = Double.parseDouble(parameter);
                            continue;
                        }
                        if (parameterType == java.lang.Float.class) {
                            argurments[index++] = Float.parseFloat(parameter);
                            continue;
                        }
                        if (parameterType == java.lang.Short.class) {
                            argurments[index++] = Short.parseShort(parameter);
                            continue;
                        }
                        if (parameterType == java.lang.Byte.class) {
                            argurments[index++] = Byte.parseByte(parameter);
                            continue;
                        }
                        if (parameterType == java.lang.Long.class) {
                            argurments[index++] = Long.parseLong(parameter);
                            continue;
                        }
                        if (parameterType == java.lang.Character.class) {
                            argurments[index++] = parameter.toCharArray()[0];
                            continue;
                        }
                        if (parameterType == java.lang.Boolean.class) {
                            argurments[index++] = Boolean.parseBoolean(parameter);
                            continue;
                        }
                        // 基本数据类型转换结束

                        /** 日期类型转换开始 */
                        if (parameterType == java.time.LocalDate.class) {
                            argurments[index++] = LocalDate.parse(parameter, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                            continue;
                        }
                        if (parameterType == java.time.LocalDateTime.class) {
                            argurments[index++] = LocalDateTime.parse(parameter, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                            continue;
                        }
                        /** 日期类型转换结束 */

                        /** 处理数组 开始*/
                        if (parameterValues != null && shuzuType != null && shuzulength >0){
                            if (shuzuType == java.lang.String.class) {
                                String[] str = new String[shuzulength];
                                for (int i = 0; i < shuzulength; i++) {
                                    str[i] = parameterValues[i];
                                }
                                argurments[index++] = str;
                                continue;
                            }
                            if (shuzuType == java.lang.Integer.class) {
                                Integer[] arr = new Integer[shuzulength];
                                for (int i = 0; i < shuzulength; i++) {
                                    arr[i] = Integer.valueOf(parameterValues[i]);
                                }
                                argurments[index++] = arr;
                                continue;
                            }
                            if (shuzuType == java.lang.Double.class) {
                                Double[] dob = new Double[shuzulength];
                                for (int i = 0; i < shuzulength; i++) {
                                    dob[i] = Double.parseDouble(parameterValues[i]);
                                }
                                argurments[index++] = dob;
                                continue;
                            }
                            if (shuzuType == java.lang.Float.class) {
                                Float[] flo = new Float[shuzulength];
                                for (int i = 0; i < shuzulength; i++) {
                                    flo[i] = Float.parseFloat(parameterValues[i]);
                                }
                                argurments[index++] = flo;
                                continue;
                            }
                            if (shuzuType == java.lang.Short.class) {
                                Short[] sho = new Short[shuzulength];
                                for (int i = 0; i < shuzulength; i++) {
                                    sho[i] = Short.valueOf(parameterValues[i]);
                                }
                                argurments[index++] = sho;
                                continue;
                            }
                            if (shuzuType == java.lang.Byte.class) {
                                Byte[] byt = new Byte[shuzulength];
                                for (int i = 0; i < shuzulength; i++) {
                                    byt[i] = Byte.valueOf(parameterValues[i]);
                                }
                                argurments[index++] = byt;
                                continue;
                            }
                            if (shuzuType == java.lang.Long.class) {
                                Long[] lon = new Long[shuzulength];
                                for (int i = 0; i < shuzulength; i++) {
                                    lon[i] = Long.valueOf(parameterValues[i]);
                                }
                                argurments[index++] = lon;
                                continue;
                            }
                            if (shuzuType == java.lang.Character.class) {
                                Character[] cha = new Character[shuzulength];
                                for (int i = 0; i < shuzulength; i++) {
                                    cha[i] = parameterValues[i].toCharArray()[0];
                                }
                                argurments[index++] = cha;
                                continue;
                            }
                            if (shuzuType == java.lang.Boolean.class) {
                                Boolean[] bol = new Boolean[shuzulength];
                                for (int i = 0; i < shuzulength; i++) {
                                    bol[i] = Boolean.valueOf(parameterValues[i]);
                                }
                                argurments[index++] = bol;
                                continue;
                            }
                            if (shuzuType == java.time.LocalDate.class) {
                                LocalDate[] lol = new LocalDate[shuzulength];
                                for (int i = 0; i < shuzulength; i++) {
                                    lol[i] = LocalDate.parse(parameterValues[i], DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                                }
                                argurments[index++] = lol;
                                continue;
                            }
                            if (shuzuType == java.time.LocalDateTime.class) {
                                LocalDateTime[] lot = new LocalDateTime[shuzulength];
                                for (int i = 0; i < shuzulength; i++) {
                                    lot[i] = LocalDateTime.parse(parameterValues[i],DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                                }
                                argurments[index++] = lot;
                                continue;
                            }
                        }
                        // 处理不了的参数 暂时以原来的值处理
                        else {
                            argurments[index++] = parameter;
                            continue;
                        }
                    } catch (Exception e) {
                        // 这里 可能会出现 类型转换异常
                        e.printStackTrace();
                        argurments[index++] = null;
                        continue;
                    }
                    /** 处理数组结束 */
                }
                else {
                    // 如果不是 我们记录的能处理的类型 那就以Java对象转化
                    if (!doCopy.contains(parameterType) && shuzuType ==null) {
                        /** Java 对象 转化开始 */
                        try {
                            Object paramObj = parameterType.newInstance();
                            BeanUtils.populate(paramObj, req.getParameterMap());
                            argurments[index++] = paramObj;
                            continue;

                        } catch (Exception e) {
                            e.printStackTrace();
                            argurments[index++] = null;
                            continue;
                        }
                        /** Java 对象 转化结束 */
                    }
                }
            }
            for (int i = 0; i < argurments.length; i++) {
                System.out.println(argurments[i]);
            }
            // 执行方法
            Object modelview = method.invoke(obj, argurments);

            /**
            处理方法的返回值
            */
            if (modelview != null) {
                // 判断是不是 String
                if (modelview instanceof String) {
                    String viewname = (String) modelview;
                    // 如果是重定向，走 重定向
                    if (viewname.startsWith("redirect:")) {
                        // 重定向 路径拼接 并发送重定向
                        // 这里有个 小Bug 没有 处理Https 协议，以及内外部资源 我们为了更好的处理，我们直接默认http 以及内部资源
                        resp.sendRedirect(viewname.replace("redirect:", ""));
                    }
                    // 如果 是重定向 那就走 重定向
                    else {
                        // 确保你的文件，在/WEB-INF/ 下 而不是 webapp 下 切记 我调试了 1个多小时，跳转不了，最后发现路径错了
                        req.getRequestDispatcher(prefix + "/" + viewname + suffix).forward(req, resp);
                    }
                }
                // 如果 返回 不是 String 类型的 我们现在处理 ：按照Json 进行处理
                // 1、修改 result 更名未 modelview
                else {
                    resp.setContentType("application/json;charset=UTF-8");
                    // modelview 调用 阿里的 fastJson 转为 Json 打印在浏览器上
                    resp.getWriter().write(JSON.toJSONString(modelview).toString());
                }

            }

        } catch (Exception e) {

            // 向浏览器展示 消息 利用 HttpServletResponse 的枚举 SC_INTERNAL_SERVER_ERROR 其含义 代表 500
            resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "服务端 发生错误，请联系系统管理员 Tel :110 ");

            /*
             sendError 与  getWriter 同时 出现，会执行 sendError() 方法
                resp.setStatus(500);
                resp.getWriter().println("兄弟 500 错误了 <br>");
                e.printStackTrace(resp.getWriter());
            */
            // 控制台打印信息
            e.printStackTrace();

        }
        //super.doGet(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 配置 所有请求都走 doGet 方法
        // super.doPost(req, resp);
        doGet(req, resp);
    }
}
